package com.fh.shop.api.interceptor;

import com.alibaba.fastjson.JSON;
import com.fh.shop.api.annotation.NeedLogin;
import com.fh.shop.api.common.ResponseEnum;
import com.fh.shop.api.common.ServerResponse;
import com.fh.shop.api.common.SystemConstant;
import com.fh.shop.api.exception.TokenException;
import com.fh.shop.api.member.vo.MemberVo;
import com.fh.shop.api.util.KeyUtil;
import com.fh.shop.api.util.Md5Util;
import com.fh.shop.api.util.RedisUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class LoginInterceptor extends HandlerInterceptorAdapter {

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 解决跨域问题 @CrossOrigin本质  No 'Access-Control-Allow-Origin' header
        //                                Access-Control-Allow-Origin
        // 允许所有的客户端远程访问【支持跨域】
        // 服务端 给 客户端 响应 头信息
//        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
//        // **Request header field content-type is not allowed by Access-Control-Allow-Headers**
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "content-type,x-auth,x-idempotence");
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "PUT,DELETE");
        // 处理options请求【并不是真实的请求】
        String requestMethod = request.getMethod();
        if ("options".equalsIgnoreCase(requestMethod)) {
            // 拦截
            return false;
        }
        // 判断 handler对象 是否是 HandlerMethod类型
        // swagger 中的 html页面
        if (!(handler instanceof  HandlerMethod)) {
            // 放行
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        // 获取你访问的方法[controller中的每个方法就是一个接口]
        Method method = handlerMethod.getMethod();
        // 判断接口是否需要登录才能访问
        if (!method.isAnnotationPresent(NeedLogin.class)) {
            // 获取方法上自定义注解
            // 放行
            return true;
        }
        // 怎么接受到客户端提交的信息？？？【客户端 通过 请求头 的方式来提交x.y的信息】
        // 发送自定义的头信息
        // 在后台中如何获取 客户端 提交的头信息？？？
        // 头信息 key=value [x-auth=x.y]
        String header = request.getHeader("x-auth");
        // 非空验证
        if (StringUtils.isBlank(header)) {
            // 拦截
            // 【需求:】拦截，而且响应给客户端提示信息{code:"";data:null;msg:""}
            // 抛出 自定义 异常
            throw new TokenException(ResponseEnum.TOKEN_IS_NULL);
//            return false;
        }
        // 头信息完整性验证
        String[] headerArr = header.split("\\.");
        if (headerArr.length != 2) {
            throw new TokenException(ResponseEnum.TOKEN_IS_ERROR);
        }
        // 验签
        // 组装信息 x.y
        //String result = memberVoJsonBase64 + "." + sign;
        String memberVoJsonBase64 = headerArr[0];
        String sign = headerArr[1];
        // 生成新的签名
        // 生成签名
//        String sign = Md5Util.sign(memberVoJson, SystemConstant.SECRET);
        // 解码
        String memberVoJson = null;
        try {
            memberVoJson = new String(Base64.getDecoder().decode(memberVoJsonBase64), StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new TokenException(ResponseEnum.TOKEN_DECODE_ERROR);
        }
        // 生成新签名
        String newSign = Md5Util.sign(memberVoJson, SystemConstant.SECRET);
        // 验签
        if (!newSign.equals(sign)) {
            throw new TokenException(ResponseEnum.TOKEN_SIGN_VALIDATE_ERROR);
        }
        // json转换java对象
        MemberVo memberVo = JSON.parseObject(memberVoJson, MemberVo.class);
        Long id = memberVo.getId();
        // 判断是否超时
        String key = KeyUtil.buildMemberKey(id);
        String result = RedisUtil.get(key);
        if (result == null) {
            // 这个key不存在，超时了，自动删除了
            throw new TokenException(ResponseEnum.TOKEN_IS_TIME_OUT);
        }
        // 判断uuid是否一致
        String uuid = memberVo.getUuid();
        if (!uuid.equals(result)) {
            throw new TokenException(ResponseEnum.TOKEN_IS_MISMATCH);
        }
        // 续命/重置
        RedisUtil.expire(key, SystemConstant.MEMBER_EXPIRE_TIME);
        // 将用户信息存入request中
        request.setAttribute(SystemConstant.CURR_MEMBER, memberVo);
        // 放行
        return true;
    }
}
